﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace L2Tools.L2SSAdjuster
{
    public partial class L2SSAdjusterManager : Form
    {
        #region const
        /// <summary>
        /// システムフォルダ名称
        /// </summary>
        const string SystemDirectory = @"\System";
        /// <summary>
        /// Lineage2実行ファイル名称
        /// </summary>
        const string L2exe = @"\LineageII.exe";
        /// <summary>
        /// Bitmapファイルの拡張子
        /// </summary>
        const string Bitmap = ".bmp";
        /// <summary>
        /// Jpegファイルの拡張子
        /// </summary>
        const string Jpeg = ".jpg";
        /// <summary>
        /// 日付毎に保管する場合のサブフォルダーのフォーマット
        /// </summary>
        const string DirectoryFormat = "yyyy.MM.dd";
        /// <summary>
        /// ピリオド
        /// </summary>
        const char Period = '.';
        /// <summary>
        /// アンダーバー
        /// </summary>
        const char Underbar = '_';
        #endregion

        #region Enum
        /// <summary>
        /// アプリケーションの状態を表す
        /// </summary>
        private enum Status
        {
            Invalid = -1,
            Stoped = 0,
            Running = 1
        }
        #endregion

        #region Attribute
        /// <summary>
        /// アプリケーションの動作状態
        /// </summary>
        private Status status = Status.Stoped;
        private FileSystemWatcher watcher = null;
        #endregion

        #region Constructor
        /// <summary>
        /// インスタンスを生成する。
        /// </summary>
        public L2SSAdjusterManager()
        {
            InitializeComponent();
            LoadSettings();
            UpdateStatus();
            if (status == Status.Running)
            {
                StartAdjusting();
            }
        }
        #endregion

        #region Method

        /// <summary>
        /// SS自動整理を開始する。
        /// </summary>
        /// <remarks>
        /// 既にSystemフォルダに存在するJpeg・Bitmapファイルが存在した場合まずは
        /// 全て移動した上で、FileSystemWatcherを利用してフォルダの監視を開始する。
        /// </remarks>
        private void StartAdjusting()
        {
            string[] files = Directory.GetFiles(l2Directory.Text + SystemDirectory);
            foreach (string file in files)
            {
                if (file.ToLower().EndsWith(Bitmap) || file.ToLower().EndsWith(Jpeg))
                {
                    AdjustFile(file);
                }
            }

            watcher = new FileSystemWatcher();
            watcher.NotifyFilter = NotifyFilters.FileName;
            watcher.Path = l2Directory.Text + SystemDirectory;
            watcher.Filter = "";
            watcher.IncludeSubdirectories = false;

            watcher.Created += new FileSystemEventHandler(WacherCreated);

            watcher.EnableRaisingEvents = true;
        }

        /// <summary>
        /// SS自動監視を終了する。
        /// </summary>
        private void StopAdjusting()
        {
            if (watcher != null)
            {
                watcher.EnableRaisingEvents = false;
                watcher.Dispose();
                watcher = null;
            }
        }


        /// <summary>
        /// Systemフォルダ内に新たなファイルが作成された際に、そのイベントをハンドルして
        /// 処理を行う。
        /// </summary>
        /// <param name="sourceObject"></param>
        /// <param name="e"></param>
        private void WacherCreated(object sourceObject, FileSystemEventArgs e)
        {
            if (e.FullPath.ToLower().EndsWith(Jpeg) || e.FullPath.ToLower().EndsWith(Bitmap))
            {
                AdjustFile(e.FullPath);
            }
        }

        /// <summary>
        /// 指定されたファイルを整理（リネーム＆移動）します。
        /// </summary>
        /// <param name="path"></param>
        private void AdjustFile(string path)
        {
            lock (this)
            {
                while (true)
                {
                    try
                    {
                        File.Move(path, CreateFileName(path));
                        break;
                    }
                    catch (NotSupportedException)
                    {
                        MessageBox.Show("ファイル名のフォーマットがシステムでサポートされていません。\n修正をお願いします。");
                        status = Status.Stoped;
                        UpdateStatus();
                        break;
                    }
                    catch (IOException)
                    {
                        // 移動元の書き込みが終了していない場合に発生する。
                        // 無視して無限ループでできるまでトライする。
                    }
                }
            }
        }

        /// <summary>
        /// 移動後のファイルのフルパス名を生成する。
        /// </summary>
        /// <remarks>
        /// 基本、ファイルのタイムスタンプからファイル名を生成するが、同一時刻のファイル
        /// が存在した場合は、末尾に数値を1から順に付与する。
        /// </remarks>
        /// <param name="path">移動前のファイルのフルパス</param>
        /// <returns>移動先のファイルのフルパス名</returns>
        private string CreateFileName(string path)
        {
            using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                stream.Close();
            }
            DateTime lastWriteTime = File.GetLastWriteTime(path);

            string moveDirectory;
            if (createSubDirectory.Checked)
            {
                string subDirectoryName = lastWriteTime.ToString(DirectoryFormat);
                moveDirectory = ssDirectory.Text + '\\' + subDirectoryName;
                if (!Directory.Exists(moveDirectory))
                {
                    Directory.CreateDirectory(moveDirectory);
                }
            }
            else
            {
                moveDirectory = ssDirectory.Text;
            }


            string fileName = null;
            for (int i = 0; i < int.MaxValue; i++)
            {
                if (i == 0)
                {
                    fileName = lastWriteTime.ToString(fileNameFormat.Text);
                }
                else
                {
                    fileName = lastWriteTime.ToString(fileNameFormat.Text) + Underbar + i;
                }
                if (path.EndsWith(Jpeg))
                {
                    fileName += Jpeg;
                }
                else
                {
                    fileName += Bitmap;
                }
                if (!File.Exists(moveDirectory + '\\' + fileName))
                {
                    break;
                }
            }

            return moveDirectory + '\\' + fileName;
        }

        /// <summary>
        /// アプリケーション設定ファイルより設定情報をロードする。
        /// </summary>
        private void LoadSettings()
        {
            global::L2Tools.L2SSAdjuster.Properties.Settings settings = global::L2Tools.L2SSAdjuster.Properties.Settings.Default;

            l2Directory.Text            = settings.L2Directory;
            ssDirectory.Text            = settings.SSDirectory;
            createSubDirectory.Checked  = settings.CreateSubDirectory;
            fileNameFormat.Text         = settings.FileNameFormat;
            status                      = (Status)settings.Status;
        }

        /// <summary>
        /// アプリケーション設定ファイルに設定情報をセーブする。
        /// </summary>
        private void SaveSettings()
        {
            global::L2Tools.L2SSAdjuster.Properties.Settings settings = global::L2Tools.L2SSAdjuster.Properties.Settings.Default;

            settings.L2Directory        = l2Directory.Text;
            settings.SSDirectory        = ssDirectory.Text;
            settings.CreateSubDirectory = createSubDirectory.Checked;
            settings.FileNameFormat     = fileNameFormat.Text;
            settings.Status             = (int)status;

            settings.Save();
        }

        /// <summary>
        /// 画面の表示状態を更新する。
        /// </summary>
        private void UpdateStatus()
        {
            if (!ValidationL2Directory())
            {
                MessageBox.Show("LineageⅡフォルダを選択してください。");
                status = Status.Invalid;
            }
            else if (!ValidationSsDirectory())
            {
                MessageBox.Show("SS保管フォルダを選択してください。");
                status = Status.Invalid;
            }
            else
            {
                if (status == Status.Invalid)
                {
                    status = Status.Stoped;
                }
            }

            if (status == Status.Invalid)
            {
                selectL2Directory.Enabled   = true;
                selectSSDirectory.Enabled   = true;
                createSubDirectory.Enabled  = true;
                fileNameFormat.Enabled      = true;

                startAdjusting.Enabled      = false;
                stopAdjusting.Enabled       = false;
            }
            else if (status == Status.Running)
            {
                selectL2Directory.Enabled   = false;
                selectSSDirectory.Enabled   = false;
                createSubDirectory.Enabled  = false;
                fileNameFormat.Enabled      = false;

                startAdjusting.Enabled      = false;
                stopAdjusting.Enabled       = true;
            }
            else
            {
                selectL2Directory.Enabled   = true;
                selectSSDirectory.Enabled   = true;
                createSubDirectory.Enabled  = true;
                fileNameFormat.Enabled      = true;

                startAdjusting.Enabled      = true;
                stopAdjusting.Enabled       = false;
            }
            SaveSettings();
        }

        /// <summary>
        /// Lineage2ディレクトの設定値のチェックを行う。
        /// </summary>
        /// <returns></returns>
        private bool ValidationL2Directory()
        {
            if (!Directory.Exists(this.l2Directory.Text))
            {
                return false;
            }
            else if (!Directory.Exists(this.l2Directory.Text + SystemDirectory))
            {
                return false;
            }
            else if (!File.Exists(this.l2Directory.Text + SystemDirectory + L2exe))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// SS保管ディレクトリのチェックを行う。
        /// </summary>
        /// <returns></returns>
        private bool ValidationSsDirectory()
        {
            if (!Directory.Exists(this.ssDirectory.Text))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 終了メニューが選択された際のイベントハンドラ。
        /// アプリケーションを終了する。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClickExit(object sender, EventArgs e)
        {
            StopAdjusting();
            Application.Exit();
        }
        /// <summary>
        /// Lineage2ディレクト選択ボタンのイベントハンドラ
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClickSelectL2Directory(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();

            dialog.Description = "フォルダを指定してください。";
            dialog.RootFolder = Environment.SpecialFolder.Desktop;
            dialog.SelectedPath = l2Directory.Text;
            dialog.ShowNewFolderButton = false;

            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                if (!dialog.SelectedPath.Equals(l2Directory.Text))
                {
                    l2Directory.Text = dialog.SelectedPath;
                    UpdateStatus();
                }
            }
        }

        /// <summary>
        /// SS保管ディレクトリの選択ボタンを押下された際のイベントハンドラ
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClickSelectSSRepository(object sender, EventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();

            dialog.Description = "フォルダを指定してください。";
            dialog.RootFolder = Environment.SpecialFolder.Desktop;
            dialog.SelectedPath = ssDirectory.Text;
            dialog.ShowNewFolderButton = true;

            if (dialog.ShowDialog(this) == DialogResult.OK)
            {
                if (!dialog.SelectedPath.Equals(ssDirectory.Text))
                {
                    ssDirectory.Text = dialog.SelectedPath;
                    UpdateStatus();
                }
            }
        }

        private void ClickStartAdjusting(object sender, EventArgs e)
        {
            status = Status.Running;
            UpdateStatus();
            StartAdjusting();
        }

        private void ClickStopAdjusting(object sender, EventArgs e)
        {
            StopAdjusting();

            status = Status.Stoped;
            UpdateStatus();
        }
        #endregion
    }
}
